﻿#include "widget.h" // 包含Widget类的头文件
#include "ui_widget.h" // 包含UI类的头文件
#include <QFileDialog> // 包含文件对话框类的头文件
#include <QMessageBox> // 包含消息框类的头文件
#include <QPixmap> // 包含像素图类的头文件
#include <QGraphicsView> // 包含图形视图类的头文件
#include <QPainter> // 包含画家类的头文件
#include <QMouseEvent> // 包含鼠标事件类的头文件
#include <chrono> // 包含时间库的头文件

// Widget类的构造函数
Widget::Widget(QWidget* parent)
	: QWidget(parent) // 调用基类QWidget的构造函数
	, ui(new Ui::Widget) // 创建UI对象
{
	ui->setupUi(this); // 设置UI
	this->setWindowTitle("Pathplan"); // 设置窗口标题
	ui->widget_origin->installEventFilter(this); // 在widget_origin上安装事件过滤器
	state = NOTHING; // 初始化状态为NOTHING
	astar.rownum = ui->lineEdit_rownum->text().toInt(); // 获取行数并转换为整数
	astar.colnum = ui->lineEdit_colnum->text().toInt(); // 获取列数并转换为整数
	astar.setSize(astar.rownum, astar.colnum); // 设置地图大小

	height = 400;
	distance = 0;
	algorithm_distance = 0;


	QPixmap pixmap(":/images/start.png"); // 加载图片
	connect(ui->pushButton_setimage, SIGNAL(clicked()), this, SLOT(showimage())); // 连接按钮点击信号到槽函数
}

// Widget类的析构函数
Widget::~Widget()
{
	delete ui; // 删除UI对象
}

// 鼠标按下事件处理函数
void Widget::mousePressEvent(QMouseEvent* event)
{
	if (state != NOTHING) // 如果状态不是NOTHING
	{
		int x = event->x() - ui->widget_origin->x(); // 获取相对于widget_origin的x坐标
		int y = event->y() - ui->widget_origin->y() - 10; // 获取相对于widget_origin的y坐标

		if ((x > 0) && (x < ui->widget_origin->width()) && (y > 0) && (y < ui->widget_origin->height())) // 如果坐标在widget_origin范围内
		{
			int px = y / (ui->widget_origin->height() / astar.rownum); // 计算行坐标
			int py = x / (ui->widget_origin->width() / astar.colnum); // 计算列坐标

			if (state == SETSTART) // 如果状态是设置起点
			{
				astar.setStart(px, py); // 设置起点
				if (astar.startnode != NULL) // 如果起点设置成功
				{
					ui->lineEdit_sx->setText(QString::number(px)); // 显示起点行坐标
					ui->lineEdit_sy->setText(QString::number(py)); // 显示起点列坐标
					if (astar.endnode != NULL) // 如果终点已设置
					{
						for (int i = 0; i < astar.rownum; i++) // 重置所有节点的父节点
						{
							for (int j = 0; j < astar.colnum; j++)
							{
								astar.nodemap[i][j].parent = NULL;
							}
						}
						newstart(); // 重新开始A*算法
						
					}
				}
				else // 如果起点设置失败
				{
					astar.routhlist.clear(); // 清空路径列表
					ui->lineEdit_sx->setText(""); // 清空起点行坐标显示
					ui->lineEdit_sy->setText(""); // 清空起点列坐标显示
				}
			}
			else if (state == SETEND) // 如果状态是设置终点
			{
				astar.setEnd(px, py); // 设置终点
				if (astar.endnode != NULL) // 如果终点设置成功
				{
					ui->lineEdit_ex->setText(QString::number(px)); // 显示终点行坐标
					ui->lineEdit_ey->setText(QString::number(py)); // 显示终点列坐标
					
					if (astar.startnode != NULL) // 如果起点已设置
					{
						newstart(); // 重新开始A*算法
					}
				}
				else // 如果终点设置失败
				{
					astar.routhlist.clear(); // 清空路径列表
					ui->lineEdit_ex->setText(""); // 清空终点行坐标显示
					ui->lineEdit_ey->setText(""); // 清空终点列坐标显示
				}
			}
			else if (state == SETBARRIER) // 如果状态是设置障碍物
			{
				astar.setBarrier(px, py); // 设置障碍物
				ui->lineEdit_barriernum->setText(QString::number(astar.barrierlist.size())); // 显示障碍物数量
				newstart(); // 重新开始A*算法
			}
			else // 其他状态
			{
			}
		}
	}
	update(); // 更新界面
}

// 显示绘图函数
void Widget::showPaint()
{
	QPainter p(ui->widget_origin); // 在widget_origin上创建画家对象

    dx = ui->widget_origin->width() / astar.colnum; // 计算横向网格大小
    dy = ui->widget_origin->height() / astar.rownum; // 计算纵向网格大小
	for (int i = 0; i <= astar.rownum; i++) // 画横线
	{
		if (height / astar.rownum * i % 40 == 0) // 每10个栅格画一条线
		{
			p.drawLine(0, i * dy, ui->widget_origin->width(), i * dy);
		}
	}
	for (int i = 0; i <= astar.colnum; i++) // 画纵线
	{
		if (height / astar.colnum * i % 40 == 0) // 每10个栅格画一条线
		{
			p.drawLine(i * dx, 0, i * dx, ui->widget_origin->height());
		}
	}

	if (astar.issuccess) // 如果找到路径
	{
		p.setPen(Qt::green); // 设置画笔为绿色
		// 设置画笔宽度
		p.setPen(QPen(Qt::green, 2));
		double sum = 0;
		int sx = astar.routhlist[0]->x * dy; // 计算当前路径节点的x坐标
		int sy = astar.routhlist[0]->y * dx; // 计算当前路径节点的y坐标
		//p.drawRect(sy, sx, dy, dx); // 画路径节点
		int next_sx = astar.routhlist[1]->x * dy + dx / 2; // 计算下一个路径节点的x坐标
		int next_sy = astar.routhlist[1]->y * dx + dx / 2; // 计算下一个路径节点的y坐标
		p.drawLine(sy, sx, next_sy, next_sx); // 画当前节点到下一个节点的直线
		sum = sum + std::sqrt(std::pow(next_sx - sx, 2) + std::pow(next_sy - sy, 2));
		for (int j = 1; j < astar.routhlist.size() - 2; j++) // 遍历路径列表，最后一个节点不需要画线
		{
			
			sx = astar.routhlist[j]->x * dy + dx/2; // 计算当前路径节点的x坐标
			sy = astar.routhlist[j]->y * dx + dx / 2; // 计算当前路径节点的y坐标
			next_sx = astar.routhlist[j + 1]->x * dy + dx / 2; // 计算下一个路径节点的x坐标
			next_sy = astar.routhlist[j + 1]->y * dx + dx / 2; // 计算下一个路径节点的y坐标
			p.drawLine(sy, sx, next_sy, next_sx); // 画当前节点到下一个节点的直线
			sum = sum + std::sqrt(std::pow(next_sx - sx, 2) + std::pow(next_sy - sy, 2));
		}
		sx = astar.routhlist[astar.routhlist.size() - 2]->x * dy + dx / 2; // 计算当前路径节点的x坐标
		sy = astar.routhlist[astar.routhlist.size() - 2]->y * dx + dx / 2; // 计算当前路径节点的y坐标
		//p.drawRect(sy, sx, dy, dx); // 画路径节点
		next_sx = astar.routhlist[astar.routhlist.size() - 1]->x * dy; // 计算下一个路径节点的x坐标
		next_sy = astar.routhlist[astar.routhlist.size() - 1]->y * dx; // 计算下一个路径节点的y坐标
		p.drawLine(sy, sx, next_sy, next_sx); // 画当前节点到下一个节点的直线
		sum = sum + std::sqrt(std::pow(next_sx - sx, 2) + std::pow(next_sy - sy, 2));
		ui->lineEdit_algorithm->setText(QString::number(sum));
		p.setPen(Qt::NoBrush); // 重置画刷
	}

	if (!astar.barrierlist.empty()) // 如果障碍物列表不为空
	{
		p.setBrush(Qt::black); // 设置画刷为黑色
		p.setPen(Qt::black); // 设置画笔为绿色
		for (int j = 0; j < astar.barrierlist.size(); j++) // 遍历障碍物列表
		{
			
			int sx = astar.barrierlist[j]->x * dy; // 计算障碍物节点的x坐标
			int sy = astar.barrierlist[j]->y * dx; // 计算障碍物节点的y坐标
			p.drawRect(sy, sx, dy, dx); // 画障碍物节点
			
		}
		p.setPen(Qt::NoBrush); // 重置画刷
	}

	if (astar.startnode != NULL) // 如果起点已设置
	{
		//p.setBrush(Qt::red); // 设置画刷为红色
		//int sx = astar.startnode->x * dy; // 计算起点节点的x坐标
		//int sy = astar.startnode->y * dx; // 计算起点节点的y坐标
		//p.drawRect(sy - 3, sx - 3, dy + 3, dx + 3); // 画起点节点
		//p.setBrush(Qt::NoBrush); // 重置画刷
		p.fillRect(astar.startnode->y * dx, astar.startnode->x * dy, dx, dy, Qt::red);
	}

	if (astar.endnode != NULL) // 如果终点已设置
	{
		//p.setBrush(Qt::blue); // 设置画刷为蓝色
		//int sx = astar.endnode->x * dy; // 计算终点节点的x坐标
		//int sy = astar.endnode->y * dx; // 计算终点节点的y坐标
		//p.drawRect(sy - 3, sx - 3, dy + 3, dx + 3); // 画终点节点
		//ui->lineEdit_distance->setText(QString::number(std::sqrt(std::pow(astar.startnode->x * dy - sx, 2) + std::pow(astar.startnode->y * dx - sy, 2)))); // 显示直线距离
		//p.setBrush(Qt::NoBrush); // 重置画刷
		p.fillRect(astar.endnode->y * dx, astar.endnode->x * dy, dx, dy, Qt::blue);
	}
}

// 事件过滤器函数
bool Widget::eventFilter(QObject* watched, QEvent* event)
{
	if (watched == ui->widget_origin && event->type() == QEvent::Paint) // 如果事件是widget_origin的绘制事件
	{
		showPaint(); // 调用显示绘图函数
	}
	return QWidget::eventFilter(watched, event); // 调用基类的事件过滤器函数
}

// 重新开始A*算法函数
void Widget::newstart()
{
	if(astar.endnode == NULL && astar.startnode == NULL)
		QMessageBox::information(this, "Missing starting or ending point", "Please add the start and end points."); // 显示错误信息
	else
	{
		auto start = std::chrono::high_resolution_clock::now(); // 记录开始时间
		QString currentText = ui->comboBox->currentText();
		astar.openlist.clear(); // 清空开放列表
		astar.closelist.clear(); // 清空关闭列表
		astar.routhlist.clear(); // 清空路径列表
		astar.surrandlist.clear(); // 清空邻近节点列表
		astar.issuccess = false; // 重置成功标志
		update(); // 更新界面
		astar.insertList(&astar.openlist, astar.startnode); // 将起点插入开放列表
		if (currentText == "A*") {

			astar.step(); // 执行A*算法的一步
			if (!astar.issuccess)
				QMessageBox::information(this, "Path Found", "No path found, check the start and end points."); // 显示错误信息
		}
		else if (currentText == "JPS") {
			int** pMap;//地图二维指针数组
			pMap = new int* [astar.rownum];
			for (int i = 0; i < astar.rownum; i++) {
				pMap[i] = new int[astar.colnum];
				for (int j = 0; j < astar.colnum; j++) {
					if (astar.isInList(&astar.barrierlist, &astar.nodemap[i][j])) pMap[i][j] = 1;
					else pMap[i][j] = 0;
					//cout<<pMap[i][j];
				}
				//cout<<endl;
			}
			Jps::PathNode jStart = { astar.startnode->x, astar.startnode->y };
			Jps::PathNode jEnd = { astar.endnode->x, astar.endnode->y };
			jps.Init(pMap, astar.colnum, astar.rownum);

			// 假设 jps.FindPath 返回 std::vector<Jps::PathNode>
			std::vector<Jps::PathNode> path = jps.FindPath(jStart, jEnd);
            if(path.size() != 0){
                astar.issuccess = true; // 重置成功标志
                // 遍历 path，将每个节点的 row 和 col 存储到 astar.routhlist 中
                for (const auto& node : path) {
                    // 创建 Node 对象并设置其 x 和 y 成员变量
                    Node* newNode = new Node{ node.row, node.col };
                    // 将新创建的 Node 对象的指针推入 astar.routhlist
                    astar.routhlist.push_back(newNode);
                }
            }

		}
        else if (currentText == "APF") {
            apf.InitializeMap(astar.colnum, astar.rownum);
            APF::Node start{astar.startnode->x, astar.startnode->y };
            APF::Node end{astar.endnode->x, astar.endnode->y};
            apf.SetStartEnd(start, end);
            for (int i = 0; i < astar.rownum; i++) {
                for (int j = 0; j < astar.colnum; j++) {
                    if (astar.isInList(&astar.barrierlist, &astar.nodemap[i][j])) apf.AddObstacle(i, j);
                }
                //cout<<endl;
            }
            // 设置最大迭代次数
            apf.SetMaxIterations(300);
            std::vector<APF::Node> path = apf.PlanPath();
            if(path.size() != 0){
                astar.issuccess = true; // 重置成功标志
                // 遍历 path，将每个节点的 row 和 col 存储到 astar.routhlist 中
                for (const auto& node : path) {
                    // 创建 Node 对象并设置其 x 和 y 成员变量
                    Node* newNode = new Node{ node.x, node.y };
                    // 将新创建的 Node 对象的指针推入 astar.routhlist
                    astar.routhlist.push_back(newNode);
                }
            }

        }
		else if(currentText == "SFJPS") {
			int** pMap;//地图二维指针数组
			pMap = new int* [astar.rownum];
			for (int i = 0; i < astar.rownum; i++) {
				pMap[i] = new int[astar.colnum];
				for (int j = 0; j < astar.colnum; j++) {
					if (astar.isInList(&astar.barrierlist, &astar.nodemap[i][j])) pMap[i][j] = 1;
					else pMap[i][j] = 0;
					//cout<<pMap[i][j];
				}
				//cout<<endl;
			}
			Sfjps::PathNode jStart = { astar.startnode->x, astar.startnode->y };
			Sfjps::PathNode jEnd = { astar.endnode->x, astar.endnode->y };
			sfjps.Init(pMap, astar.colnum, astar.rownum);

			// 假设 jps.FindPath 返回 std::vector<Jps::PathNode>
			std::vector<Sfjps::PathNode> path = sfjps.FindPath(jStart, jEnd);
            if(path.size() != 0){
                astar.issuccess = true; // 重置成功标志
                // 遍历 path，将每个节点的 row 和 col 存储到 astar.routhlist 中
                for (const auto& node : path) {
                    // 创建 Node 对象并设置其 x 和 y 成员变量
                    Node* newNode = new Node{ node.row, node.col };
                    // 将新创建的 Node 对象的指针推入 astar.routhlist
                    astar.routhlist.push_back(newNode);
                }
            }
		}
		auto end = std::chrono::high_resolution_clock::now(); // 记录结束时间
		std::chrono::duration<double> elapsed = end - start; // 计算消耗的时间
		// 记录消耗的时间，保留小数点2位
		astar.spend_time = elapsed.count(); 
		ui->lineEdit_spend_time->setText(QString::number(astar.spend_time, 'f', 2)); // 显示消耗的时间
        ui->lineEdit_distance->setText(QString::number(std::sqrt(std::pow((astar.startnode->x - astar.endnode->x)*dx , 2) + std::pow((astar.startnode->y - astar.endnode->y)*dy , 2)))); // 显示直线距离
	}
	
}

// 重置按钮点击事件处理函数
void Widget::on_pushButton_reset_clicked()
{
	astar.reSet(); // 重置A*算法
	ui->lineEdit_sx->setText(""); // 清空起点行坐标显示
	ui->lineEdit_sy->setText(""); // 清空起点列坐标显示
	ui->lineEdit_ex->setText(""); // 清空终点行坐标显示
	ui->lineEdit_ey->setText(""); // 清空终点列坐标显示
	ui->lineEdit_barriernum->setText(QString::number(astar.barrierlist.size())); // 显示障碍物数量
	astar.rownum = ui->lineEdit_rownum->text().toInt(); // 获取行数并转换为整数
	astar.colnum = ui->lineEdit_colnum->text().toInt(); // 获取列数并转换为整数
	ui->lineEdit_spend_time->setText("0"); // 重置消耗时间显示
	astar.setSize(astar.rownum, astar.colnum); // 设置地图大小
	ui->pushButton_start->setEnabled(false); // 禁用开始按钮
	update(); // 更新界面
}

// 设置起点按钮点击事件处理函数
void Widget::on_pushButton_setstart_clicked()
{
	if ((astar.rownum >= 1) && (astar.colnum >= 1) && (astar.rownum + astar.colnum >= 2)) // 如果行数和列数有效
	{
		state = SETSTART; // 设置状态为设置起点
	}
	else
	{
		state = NOTHING; // 设置状态为NOTHING
	}
}

// 设置终点按钮点击事件处理函数
void Widget::on_pushButton_setend_clicked()
{
	if ((astar.rownum >= 1) && (astar.colnum >= 1) && (astar.rownum + astar.colnum >= 2)) // 如果行数和列数有效
	{
		state = SETEND; // 设置状态为设置终点
	}
	else
	{
		state = NOTHING; // 设置状态为NOTHING
	}
}

// 设置障碍物按钮点击事件处理函数
void Widget::on_pushButton_setbarrier_clicked()
{
	if ((astar.rownum >= 1) && (astar.colnum >= 1) && (astar.rownum + astar.colnum >= 2)) // 如果行数和列数有效
	{
		state = SETBARRIER; // 设置状态为设置障碍物
	}
	else
	{
		state = NOTHING; // 设置状态为NOTHING
	}
}

// 行数文本框内容改变事件处理函数
void Widget::on_lineEdit_rownum_textChanged(const QString& arg1)
{
	astar.rownum = arg1.toInt(); // 获取行数并转换为整数
	astar.setSize(astar.rownum, astar.colnum); // 设置地图大小
	update(); // 更新界面
}

// 列数文本框内容改变事件处理函数
void Widget::on_lineEdit_colnum_textChanged(const QString& arg1)
{
	astar.colnum = arg1.toInt(); // 获取列数并转换为整数
	astar.setSize(astar.rownum, astar.colnum); // 设置地图大小
	update(); // 更新界面
}



// 确认按钮点击事件处理函数
void Widget::on_pushButton_ok_clicked()
{
	if (astar.startnode != NULL && astar.endnode != NULL) // 如果起点和终点已设置
	{
		ui->pushButton_start->setEnabled(true); // 启用开始按钮
	}
}

// 开始按钮点击事件处理函数
void Widget::on_pushButton_start_clicked()
{
	newstart();
	//astar.insertList(&astar.openlist, astar.startnode); // 将起点插入开放列表
	//astar.step(); // 执行A*算法的一步
	//update(); // 更新界面
}

// 显示图像函数
void Widget::showimage() {
	astar.map_fileName = QFileDialog::getOpenFileName(this, "Open Image", "", "Image Files (*.jpg *.png *.bmp)"); // 打开文件对话框选择图像文件
	if (!astar.map_fileName.isEmpty()) {
		QImage image(astar.map_fileName);
		if (image.isNull()) {
			QMessageBox::information(this, "Image Viewer", "Cannot load " + astar.map_fileName);
			return;
		}
		QPixmap pixmap = QPixmap::fromImage(image);
		QPixmap scaledPixmap = pixmap.scaled(height, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
		ui->label_change->setPixmap(scaledPixmap);
	}
	ui->lineEdit_grayValue->setEnabled(true); // 启用灰度值文本框

}


void Widget::on_pushButton_gray_clicked()
{
	astar.barrierlist.clear();//清除障碍物
	//update(); // 更新界面
	astar.Astar_grayValue = ui->lineEdit_grayValue->text().toInt();
	if (!astar.map_fileName.isEmpty()) // 如果文件名不为空
	{
		QImage image(astar.map_fileName); // 加载图像
		if (astar.map_fileName.isNull()) // 如果图像加载失败
		{
			QMessageBox::information(this, "Image Viewer", "Cannot load " + astar.map_fileName); // 显示错误信息
			return; // 返回
		}

		// 将图像转换为灰度图
		QImage grayImage = image.convertToFormat(QImage::Format_Grayscale8);

		// 调整图像大小为500x500
		QImage scaledGrayImage = grayImage.scaled(height, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

		// 创建一个新的500x500的QImage用于显示网格
		QImage gridImage(height, height, QImage::Format_ARGB32);
		gridImage.fill(Qt::transparent);
		QPainter painter(&gridImage);
		painter.setPen(Qt::black);

		// 计算每个网格的宽度和高度
		int gridWidth = height / astar.colnum;
		int gridHeight = height / astar.rownum;

		// 根据灰度值绘制100x100的网格地图
		for (int y = 0; y < astar.rownum; ++y) {
			for (int x = 0; x < astar.colnum; ++x) {
				int sumGrayValue = 0;
				for (int gy = 0; gy < gridHeight; ++gy) {
					for (int gx = 0; gx < gridWidth; ++gx) {
						QRgb pixel = scaledGrayImage.pixel(x * gridWidth + gx, y * gridHeight + gy);
						sumGrayValue += qGray(pixel);
					}
				}
				int avgGrayValue = sumGrayValue / (gridWidth * gridHeight);
				if (avgGrayValue > astar.Astar_grayValue) { 
					astar.setBarrier(y, x); // 设置障碍物
					painter.fillRect(x * gridWidth, y * gridHeight, gridWidth, gridHeight, Qt::black);
				}
			}
		}
		ui->lineEdit_barriernum->setText(QString::number(astar.barrierlist.size())); // 显示障碍物数量
		//newstart(); // 重新开始A*算法
		state = SETBARRIER; // 设置状态为设置障碍物
		update(); // 更新界面
		//ui->pushButton_setstart->setEnabled(true);
		// 将生成的网格图像转换为QPixmap并显示在label_change上
		/*QPixmap gridPixmap = QPixmap::fromImage(gridImage);
		ui->label_change->setPixmap(gridPixmap);*/
	}
	else // 文件名为空
	{
		//ui->pushButton_setstart->setDisabled(true);
		QMessageBox::information(this, "Image Viewer", "Please select an image first."); // 显示错误信息
	}
}
